mod read;

use std::convert::TryInto;
use std::io::{BufReader, BufWriter, Read, Seek, Write};
use std::string::String;

use crate::io::read::*;

#[derive(Debug)]
pub enum IOError {
    ReadError(String),
    ReadCompleted(u64, u64),
}

fn vec_to_be_u64(v: &Vec<u8>) -> Result<u64, String> {
    match v.len() <= 8 {
        true => {
            let mut r: u64 = 0;
            if v.len() > 0 {
                let mut v_ = Vec::from(v.as_slice());

                for _ in v.len()..8 {
                    v_.insert(0, 0);
                }
                r = u64::from_be_bytes(v_.try_into().unwrap());
            }
            Ok(r)
        }
        false => Err(format!("data length {} > 8", v.len()).to_string()),
    }
}

impl std::fmt::Display for IOError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ReadError(s) => write!(f, "{}", s),
            Self::ReadCompleted(start, limit) => {
                write!(f, "Read Completed start: {:#010x}, limit: {}", start, limit)
            }
        }
    }
}

pub struct DataReader<R: Read + Seek> {
    reader: BufReader<R>,
    offset: u64,
}

impl<R: Read + Seek> std::fmt::Debug for DataReader<R> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "offset: 0x{:08x}, 0b{:064b}", self.offset, self.offset)
    }
}

impl<R: Read + Seek> DataReader<R> {
    pub fn open(reader: R) -> DataReader<R> {
        DataReader {
            reader: BufReader::new(reader),
            offset: 0,
        }
    }

    pub fn offset(&self) -> u64 {
        self.offset
    }

    pub fn skip(&mut self, offset: u64) -> u64 {
        self.offset += offset;
        self.offset
    }
    pub fn read(&mut self, limit: u64) -> Result<Vec<u8>, IOError> {
        let r = read_data(&mut self.reader, self.offset, limit);
        if r.is_ok() {
            self.skip(limit);
        }
        r
    }

    pub fn read_to_u64(&mut self, limit: u64) -> Result<u64, IOError> {
        match self.read(limit) {
            Ok(v) => {
                let mut r: u64 = 0;
                if v.len() > 0 {
                    let mut v_ = Vec::from(v);

                    for _ in limit..8 {
                        v_.insert(0, 0);
                    }
                    r = u64::from_be_bytes(v_.try_into().unwrap())
                }
                Ok(r)
            }
            Err(e) => Err(e),
        }
    }

    pub fn read_to_u64_not_skip(&mut self, limit: u64) -> Result<u64, IOError> {
        match read_data(&mut self.reader, self.offset, limit) {
            Ok(v) => match vec_to_be_u64(&v) {
                Ok(v) => Ok(v),
                Err(msg) => Err(IOError::ReadError(msg)),
            },
            Err(e) => Err(e),
        }
    }

    pub fn read_u64(&mut self) -> Result<u64, IOError> {
        let r = read_u64(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(8);
        }
        r
    }

    pub fn read_i64(&mut self) -> Result<i64, IOError> {
        let r = read_i64(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(8);
        }
        r
    }

    pub fn read_f64(&mut self) -> Result<f64, IOError> {
        let r = read_f64(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(8);
        }
        r
    }

    pub fn read_u32(&mut self) -> Result<u32, IOError> {
        let r = read_u32(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(4);
        }
        r
    }

    pub fn read_i32(&mut self) -> Result<i32, IOError> {
        let r = read_i32(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(4);
        }
        r
    }

    pub fn read_f32(&mut self) -> Result<f32, IOError> {
        let r = read_f32(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(4);
        }
        r
    }

    pub fn read_u16(&mut self) -> Result<u16, IOError> {
        let r = read_u16(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(2);
        }
        r
    }

    pub fn read_i16(&mut self) -> Result<i16, IOError> {
        let r = read_i16(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(2);
        }
        r
    }

    pub fn read_u8(&mut self) -> Result<u8, IOError> {
        let r = read_u8(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(1);
        }
        r
    }

    pub fn read_i8(&mut self) -> Result<i8, IOError> {
        let r = read_i8(&mut self.reader, self.offset);
        if r.is_ok() {
            self.skip(1);
        }
        r
    }

    pub fn read_string(&mut self, limit: u64) -> Result<String, IOError> {
        let r = read_string(&mut self.reader, self.offset, limit);
        if r.is_ok() {
            self.skip(limit);
        }
        r
    }
}

pub struct DataWriter<W: Write> {
    writer: BufWriter<W>,
    offset: u64,
}

impl<W: Write> DataWriter<W> {
    pub fn new(writer: W) -> DataWriter<W> {
        DataWriter {
            writer: BufWriter::new(writer),
            offset: 0,
        }
    }

    pub fn write(&mut self, data: &Vec<u8>) -> std::io::Result<usize> {
        self.writer.write(data.as_slice())
    }
}
